package com.bootdo;



import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import com.bootdo.check.domain.VideoDO;
import com.bootdo.check.service.DetectService;
import com.bootdo.check.service.VideoService;
import com.bootdo.download.controller.StaticSet;
import com.bootdo.download.domain.UrlJsonModel;



@Component
public class Asyncer {
	
	@Value("${download.path}")
	String basePath;
	@Autowired
	DetectService ds;
	
	@Autowired
	VideoService vs;
	
	VideoDO vsdo = null;
	
	
	private static final Logger logger = LoggerFactory.getLogger(Asyncer.class);
	
	
    @Async  
    public Future<String> doTask() throws InterruptedException{ 
    	UrlJsonModel urljson;
    	synchronized(StaticSet.stack) {
    		urljson = StaticSet.stack.pop();
		}
    	
    	 boolean flat =false; 
    	 
   	 try {
	   		flat = doit(urljson);
	   		 
	 		} catch (Exception e) {
	 		// TODO Auto-generated catch block
	 		 
	 		logger.error("异常错误："+e.getMessage());	
	 		e.printStackTrace();
	 	
	 	}
   	    if (!flat) {
   	     StaticSet.f =  StaticSet.f-1;
   	    }
   	  

        return new AsyncResult<>("accomplished");
    } 
    


   boolean doit(UrlJsonModel urljson) throws IOException {
	String videoUrl = urljson.getUrl();
	String orgUrl = urljson.getOriginUrl();
	
	
	logger.info("开始orgUrl任务："+orgUrl);
	logger.info("开始videoUrl任务："+videoUrl);
	String host = getHost(orgUrl);
	String hostPath = basePath+"/"+host;
	String filerecoder = hostPath+"/recoder.txt";
	
	// 过滤无效的url
//	if () {
//		return false;
//	}
	// 
	
	if (!createDir(basePath)) {
		 return false;
	}		
	if (!createDir(hostPath)) {
		 return false;
	}
	String md5 = EncoderByMd5(orgUrl);
	String videoPath = hostPath+"/"+md5;
	if (!createDir(videoPath)) {
		 return false;
	}	
	
	videoUrl = paseUrl(videoUrl);
	

	
	
	if (!(videoUrl.contains(".mp4?") || videoUrl.endsWith(".mp4") || videoUrl.endsWith(".m3u8") )) {
		logger.info("未找到相关视频："+videoUrl);
		write(videoPath+"/error.txt","下载失败："+orgUrl+"  视频地址:"+videoUrl+"\n");	  
		return false;
	}
	
	// 提取任务id
    String ids = "";
    
    String regex = "zcahvideoid=[0-9]{1,}";
    Pattern p = Pattern.compile(regex);  
    Matcher m = p.matcher(orgUrl);  
  
    while(m.find()){  
        ids=m.group(0).substring(12, m.group(0).length());
    }

    if (ids.equals("")) {
    	logger.info("提取id出错："+orgUrl);
    	return false;
    }
    
    String name = videoUrl.split("/")[videoUrl.split("/").length-1];
    name =name.split("[?]")[0];
    String imageName =  videoPath+"/"+name;
    
	// 如果是百度百科的处理方式
    if (videoUrl.endsWith(".m3u8")) {
    	List fpaths = new LinkedList<String>(); 
        Boolean fg = true;
        int i = 0;
        String resfile = imageName+".mp4";
        while(fg) {
        	String v = videoUrl+".0."+i+".ts";
            String name1 = v.split("/")[v.split("/").length-1];
            name1 =name1.split("[?]")[0];
            String newName =  videoPath+"/"+name1;
        	
        	if (!download(v,videoPath,orgUrl,ids,newName)) {
        		break;
        	}
        	fpaths.add(newName);
        	i = i+1;
        }
        
        File f = new File(resfile);
        if (!f.exists()) {
        	f.createNewFile();
        }


        // 组装成新的文件
       if (mergeFiles(fpaths,resfile)) {
    	   
 	      vsdo = vs.get(Integer.valueOf(ids));
 	      vsdo.setStatus("下载完成");
 	      vsdo.setVideopath(resfile);
 	      vs.update(vsdo);
         StaticSet.f =  StaticSet.f-1;
   		logger.info("开始检测："+resfile);	
	    ds.detect(resfile,Integer.valueOf(ids));
	    return true;
       } 
       logger.info("合并失败");	
       return false;
        
    	
    }
    

    
    imageName =  imageName+"_"+System.currentTimeMillis();	
	
	if (download(videoUrl,videoPath,orgUrl,ids,imageName)) {
		
	      vsdo = vs.get(Integer.valueOf(ids));
	      vsdo.setStatus("下载完成");
	      vsdo.setVideopath(imageName);
	      vs.update(vsdo);

        StaticSet.f =  StaticSet.f-1;
	    write(filerecoder,md5+","+orgUrl); 	    

		logger.info("开始检测："+imageName);	
		
	    ds.detect(imageName,Integer.valueOf(ids));
	    return true;
	}

    return false;
}


 void write(String fileName,String str) {
	 try {
		FileWriter writer=new FileWriter(fileName,true);
		writer.write(str+"\n");
		writer.close();
		} catch (IOException e) {
		// TODO Auto-generated catch block
		logger.error("文件写入错误："+e.getMessage());	
	
	}
	 
 }
 
	//写入记录文件
 String read(String fileName) {
	 
	  String line2="";
	  File file = new File(fileName);
	  if (!file.exists()) {
		  return line2;
	  }
	   	
	BufferedReader in;
	  
	try {
		 in = new BufferedReader(new FileReader(fileName));
		String  line=in.readLine();
		 while (line!=null) {
			 line2 =line; 
			 line=in.readLine();
		 }
		 in.close();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		logger.error("文件写入错误："+e.getMessage());	
	}
	return line2;
 }

//创建目录
Boolean createDir(String path) {
	 File file = new File(path);
	 
	 if(file.exists()) {
		
		 
	 } else {
		 if (!file.mkdirs()) {
		     logger.error("文件创建异常："+path);
			 return false;
		 }
	 }
	 
	 return true;
		
}


String getHost(String orgUrl){
	String host = "";
	// 获得域名地址
    if (orgUrl.contains("//")) {
        try {
            java.net.URL  url = new  java.net.URL(orgUrl);
            host = url.getHost();
        } catch (MalformedURLException e) {
           
            host = orgUrl;
        }
    } else {
    	host = orgUrl;
    }
    return host; 
}


  boolean download(String urlList,String path,String orgUrl,String ids,String imageName) {
	  
	  // System.out.println(urlList);
	  //  urlList = paseUrl(urlList);
	    
//	    if (StaticSet.set.contains(urlList)) {
//	        System.out.println("已经爬去过了，退出爬取："+urlList);
//	        return ;
//	    }
//        URL url = null;
//        int imageNumber = 0;
        //urllist 是否是特殊的 特殊的是多个视频 否则是单个视频


	  
	  
	  CloseableHttpClient client = HttpClients.createDefault();
	  RequestConfig config = null;
	  //使用代理

//	  if(null != proxy && StringUtils.isNotBlank(proxy.ip) && proxy.port > 0){
//	      HttpHost proxy = new HttpHost(proxy.ip, proxy.port);  
//	      config = RequestConfig.custom().setProxy(proxy).build(); 
//	  }else{
//	  //没有代理，使用默认值
	      config = RequestConfig.custom().build();
//	  }
	  //目标文件url
	  HttpGet httpGet = new HttpGet(urlList);
	  httpGet.setConfig(config);
	  //下载需登陆，设置登陆后的cookie
	 // httpGet.addHeader("Cookie", cookie);

	  try {
	      HttpResponse respone = client.execute(httpGet);
	      if(respone.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
			    logger.error("下载失败："+imageName+"  视频地址:"+urlList);		
			    write(path+"/error.txt","下载失败："+imageName+"  视频地址:"+urlList+"\n");			  
		      	client.close();
				return false;
	      }
	      
	      logger.info("开始下载："+ids);
	      vsdo = vs.get(Integer.valueOf(ids));
	      vsdo.setStatus("开始下载");
	      vs.update(vsdo);
	      logger.info("开始下载："+urlList);
	      logger.info("=============："+vsdo.getStatus()+":"+vsdo.getSheetname());
	      
	      HttpEntity entity = respone.getEntity();
	      if(entity != null) {
	          InputStream is = entity.getContent();
	          File file = new File(imageName);
	          BufferedInputStream in=new BufferedInputStream(is,8*1024*1024);
	          BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream(file),8*1024*1024);
	          int c=0;
	          while((c = in.read() )!= -1){
	        //System.out.println(Thread.currentThread().getName()+":"+c);
	              out.write(c);
	              out.flush();
	          }
	          out.close();
	          is.close();
	         
	      }
	      

	      logger.info("下载完成："+imageName+"  视频地址:"+urlList);
	   
	  } catch (Exception e) {
	      vsdo = vs.get(Integer.valueOf(ids));
		  vsdo.setStatus("下载失败");
		  vs.update(vsdo);
		   logger.error("下载失败："+imageName+"  视频地址:"+urlList);
	      	write(path+"/error.txt","下载失败："+imageName+"  视频地址:"+urlList+"\n");
			e.printStackTrace();
			return false;
	  }finally{
	      try {
	          client.close();
	      } catch (IOException e) {
	          e.printStackTrace();
	      }
	  }
	  
	  return true;
//        } catch (MalformedURLException e) {
//  
//        } catch (IOException e) {
//        	write(path+"/error.txt","下载失败："+imageName+"\n");
//            e.printStackTrace();
//        }
    }
 
  String EncoderByMd5(String s){
	char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9','a', 'b', 'c', 'd', 'e', 'f' };

	try {

			byte[] strTemp = s.getBytes();

			MessageDigest mdTemp = MessageDigest.getInstance("MD5");

			mdTemp.update(strTemp);

			byte[] md = mdTemp.digest();

			int j = md.length;

			char str[] = new char[j * 2];

			int k = 0;

			for (int i = 0; i < j; i++) {

			byte b = md[i];
		
			str[k++] = hexDigits[b >> 4 & 0xf];

			str[k++] = hexDigits[b & 0xf];

			}

			return new String(str);

		} catch (Exception e) {return null;}
    }

  
	  static String paseUrl(String url) {
		  if (url.endsWith("mp4Seg1-Frag1") && url.contains("people.com.cn")) {
			   logger.info("这是人民网的分段视频");
			  url =  url.replace("hds-vod/", "");
			  url = url.replaceAll("Seg1-Frag1", "");
	
		  }
		  System.out.println(url);
		  // 确保1小时内不爬取 相同地址的视频
		  
		  return url;
		  
	  }
	  
	  
	  public static boolean mergeFiles(List<String> fpaths, String resultPath) {
		  logger.info("开始合并视频");
		    if (fpaths == null || fpaths.size() < 1) {
		    	logger.info("开始合并视频1");
		        return false;
		    }
		    if (fpaths.size() == 1) {
		        return new File(fpaths.get(0)).renameTo(new File(resultPath));
		    }
		 
		    File[] files = new File[fpaths.size()];
		    for (int i = 0; i < fpaths.size(); i ++) {
		        files[i] = new File(fpaths.get(i));
		        if ( !files[i].exists() || !files[i].isFile()) {
		        	logger.info(fpaths.get(i));
		            return false;
		        }
		    }
		 
		    File resultFile = new File(resultPath);
		 
		    try {
		        FileChannel resultFileChannel = new FileOutputStream(resultFile, true).getChannel();
		        for (int i = 0; i < fpaths.size(); i ++) {
		            FileChannel blk = new FileInputStream(fpaths.get(i)).getChannel();
		            resultFileChannel.transferFrom(blk, resultFileChannel.size(), blk.size());
		            blk.close();
		        }
		        resultFileChannel.close();
		    } catch (FileNotFoundException e) {
		        e.printStackTrace();
		        return false;
		    } catch (IOException e) {
		        e.printStackTrace();
		        return false;
		    }
		 
		    for (int i = 0; i < fpaths.size(); i ++) {
		        files[i].delete();
		    }
		 
		    return true;
		}
	  
	  public static void main(String[] args) {
		  String url ="http://flv4.people.com.cn/hds-vod/videofile3//pvmsvideo/2018/3/26/WeiQingCheng_ba8249f09a81b59d43dd8ded7fc85057_ms_c.mp4Seg1-Frag24";
		  paseUrl(url);
	}
  
}